package cn.yunbamboos.mybatis.mapping.methods.update;

import cn.yunbamboos.mybatis.annotation.FieldStrategy;
import cn.yunbamboos.mybatis.mapping.methods.BaseMethod;
import cn.yunbamboos.mybatis.mapping.sqlnode.CustomSetSqlNode;
import cn.yunbamboos.mybatis.mapping.sqlnode.CustomWhereSqlNode;
import cn.yunbamboos.mybatis.metadata.TableFieldInfo;
import cn.yunbamboos.mybatis.metadata.TableInfo;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.scripting.xmltags.DynamicSqlSource;
import org.apache.ibatis.scripting.xmltags.IfSqlNode;
import org.apache.ibatis.scripting.xmltags.MixedSqlNode;
import org.apache.ibatis.scripting.xmltags.SqlNode;
import org.apache.ibatis.scripting.xmltags.StaticTextSqlNode;
import org.apache.ibatis.session.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

public class Update extends BaseMethod {

    private static final Logger log = LoggerFactory.getLogger(Update.class);

    public Update(Configuration configuration, TableInfo tableInfo) {
        super("update", configuration, tableInfo, SqlCommandType.INSERT);
    }

    @Override
    protected SqlSource getSqlSource() {
        String tableName = BACKTICK + this.tableInfo.getTableName() + BACKTICK;
        List<SqlNode> contents = new ArrayList<>();
        // update {} set {} WHERE {}
        // 更新表明  update tableName
        contents.add(new StaticTextSqlNode("update " + tableName));
        // 添加更新的列
        getSetSqlNode().ifPresent(contents::add);
        // 添加条件
        getWhereSqlNode().ifPresent(contents::add);


        SqlNode sqlNode = new MixedSqlNode(contents);
        return new DynamicSqlSource(configuration, sqlNode);
    }

    protected Optional<SqlNode> getSetSqlNode() {
        List<SqlNode> contents = new ArrayList<>();
        List<TableFieldInfo> fieldList = this.tableInfo.getFieldList();
        fieldList.forEach(field -> {
            // 去掉不需要更新的列
            if (!FieldStrategy.NEVER.equals(field.getUpdateStrategy())) {
                createSqlNode(field.getPropertyType(), field.getColumn(), field.getProperty(), field.getFieldStrategy()).ifPresent(contents::add);
            }
        });
        SqlNode sqlNode = new MixedSqlNode(contents);
        return Optional.of(new CustomSetSqlNode(configuration, sqlNode));
    }

    protected Optional<SqlNode> getWhereSqlNode() {
        List<SqlNode> contents = new ArrayList<>();

        this.tableInfo.getPrimaryKeyList().forEach(pk -> {
            String sql = SPACE + AND + SPACE + BACKTICK + pk.getColumn() + BACKTICK
                    + SPACE + EQUALS + SPACE
                    + HASH_LEFT_BRACE+ pk.getProperty() + RIGHT_BRACE;
            contents.add(new StaticTextSqlNode(sql));
        });

        SqlNode sqlNode = new MixedSqlNode(contents);
        return Optional.of(new CustomWhereSqlNode(configuration, sqlNode));
    }

    @SuppressWarnings("DuplicatedCode")
    private Optional<SqlNode> createSqlNode(Class<?> propertyType, String column, String property, FieldStrategy strategy) {
        if (FieldStrategy.NEVER.equals(strategy)) {
            return Optional.empty();
        }
        if (String.class.equals(propertyType)) {
            return createSqlNodeString(column, property, strategy);
        } else {
            return createSqlNodeInt(column, property, strategy);
        }
    }

    @SuppressWarnings("DuplicatedCode")
    private Optional<SqlNode> createSqlNodeInt(String column, String property, FieldStrategy strategy) {
        String text = getText(column, property);
        String notNullTest = getNotNullTest(column, property);

        StaticTextSqlNode contents = new StaticTextSqlNode(text);
        if (FieldStrategy.ALWAYS.equals(strategy)) {
            return Optional.of(contents);
        } else if (FieldStrategy.NOT_NULL.equals(strategy)
                || FieldStrategy.NOT_EMPTY.equals(strategy)
                || FieldStrategy.DEFAULT.equals(strategy)) {
            return Optional.of(new IfSqlNode(contents, notNullTest));
        }
        log.warn("int unknown column:{} strategy：{}", column, strategy);
        return Optional.empty();
    }

    @SuppressWarnings("DuplicatedCode")
    private Optional<SqlNode> createSqlNodeString(String column, String property, FieldStrategy strategy) {
        String text = getText(column, property);
        String notNullTest = getNotNullTest(column, property);
        String notEmptyTest = getNotEmptyTest(column, property);

        StaticTextSqlNode contents = new StaticTextSqlNode(text);
        if (FieldStrategy.ALWAYS.equals(strategy)) {
            return Optional.of(contents);
        } else if (FieldStrategy.NOT_NULL.equals(strategy)) {
            return Optional.of(new IfSqlNode(contents, notNullTest));
        } else if (FieldStrategy.NOT_EMPTY.equals(strategy) || FieldStrategy.DEFAULT.equals(strategy)) {
            return Optional.of(new IfSqlNode(contents, notNullTest + SPACE + AND + SPACE + notEmptyTest));
        }
        log.warn("string unknown column:{} strategy：{}", column, strategy);
        return Optional.empty();
    }

    private String getText(String column, String property) {
        // code = #{code}
        return BACKTICK + column + BACKTICK +
                SPACE + EQUALS + SPACE +
                HASH_LEFT_BRACE + property + RIGHT_BRACE + "," + SPACE;
    }

    private String getNotNullTest(String column, String property) {
        return property + SPACE + EXCLAMATION_MARK + EQUALS + SPACE + NULL;
    }

    private String getNotEmptyTest(String column, String property) {
        return property + SPACE + EXCLAMATION_MARK + EQUALS + SPACE + SINGLE_QUOTE + SINGLE_QUOTE;
    }




}